﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;

namespace PrisonStep
{
    public class Gort : BoundingCylinder
    {
        /// <summary>
        /// The model we loaded
        /// </summary>
        protected AnimatedModel animatedModel;
        public AnimatedModel AnimatedModel { get { return animatedModel; } set { animatedModel = value; } }

        /// <summary>
        /// The player orientation as a simple angle
        /// </summary>
        protected float orientation = 1.6f + (float)Math.PI;

        public bool InDoor { get { return inDoor; } }
        

        /// <summary>
        /// The transformation matrix.
        /// </summary>
        protected Matrix transform;
        public Matrix Transform { get { return transform; } set { transform = value; } }

        /// <summary>
        /// The current game
        /// </summary>
        protected PrisonGame game;

        protected bool inDoor = false;

        private TimeSpan lastShot;  //stores the time of the last laser shot

        /// <summary>
        /// If there is a pie attached to the alien, the pie is placed here (the alien can only catch one pie at a time).
        /// </summary>
        private PieFire.Pie pie = null;
        public PieFire.Pie Pie { get { return pie; } set { pie = value; } }

        private enum States
        {
            Start, StanceStart, Stance, WalkStart, WalkLoopStart, WalkLoop, TurnLoopStart, TurnLoop, EatStart, Eating
        }

        private PhibesModel phibesModel;
        private string oldRegion = "R_Section6";

        /// <summary>
        /// current state
        /// </summary>
        private States state = States.Start;

        private enum StatesWalk { TurnLeft, TurnRight, WalkAndTurnLeft, WalkAndTurnRight, Stop }
        private StatesWalk stateWalk = StatesWalk.Stop;

        public Gort(PrisonGame game, PhibesModel phibesModel)
        {
            location = new Vector3(1167, 0, -1365);
            radius = 27;
            height = 190;

            this.game = game;
            this.phibesModel = phibesModel;
            animatedModel = new AnimatedModel(game, "Gort");
            animatedModel.AddAssetClip("catcheat", "Gort-catcheat");
            animatedModel.AddAssetClip("stance", "Gort-stance");
            animatedModel.AddAssetClip("walkstart", "Gort-walkstart");
            animatedModel.AddAssetClip("walkloop", "Gort-walkloop");

            SetPlayerTransform();
            lastShot = new TimeSpan(0);
        }

        public void LoadContent(ContentManager content)
        {
            animatedModel.LoadContent(content);
            animatedModel.PlayClip("walkstart");
            animatedModel.Update(0);
            animatedModel.Speed = 1;
        }

        private void CalculateMovement(Matrix playerTransform)
        {
            Vector3 walkDirection = playerTransform.Translation - transform.Translation;
            float theta = (float)Math.Atan2(walkDirection.X, walkDirection.Z);

            //don't update the position if the alien is eating
            if (state == States.Eating || state == States.EatStart)
                return;

            theta -= orientation;
            if (theta < -(float)Math.PI)
                theta += (float)Math.PI * 2;

            //The alien is already very close to the player 
            if (walkDirection.Length() < 200 && (Math.Abs(theta) < .01))
            {
                state = States.Stance;
                stateWalk = StatesWalk.Stop;
            }
            //The alien is already very close to the player or facing more than 22.5 degrees of player
            //only rotate the alien, do not walk
            else if (walkDirection.Length() < 200 || (Math.Abs(theta) > Math.PI/2))
            {
                if (theta < (float)Math.PI && theta > 0)
                {
                    stateWalk = StatesWalk.TurnLeft;
                }
                else
                {
                    stateWalk = StatesWalk.TurnRight;
                }
                //do not restart Walk Start
                if (state == States.Start || state == States.StanceStart || state == States.Stance)
                {
                    animatedModel.Speed = 1;
                    state = States.WalkStart;
                }
            }
            //The alien needs to walk towards the player
            else
            {
                if (theta < (float)Math.PI  && theta > 0)
                {
                    stateWalk = StatesWalk.WalkAndTurnLeft;
                }
                else
                {
                    stateWalk = StatesWalk.WalkAndTurnRight;
                }
                //do not restart Walk Start
                if (state == States.Start || state == States.StanceStart || state == States.Stance)
                {
                    animatedModel.Speed = 1;
                    state = States.WalkStart;
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="gameTime"></param>
        /// <param name="playerTransform"></param>
        /// <param name="leaveDoorsOpen">Indicates if the door should close doors or leave them open</param>
        public void Update(GameTime gameTime, Matrix playerTransform, bool leaveDoorsOpen, Player victoria)
        {
            double deltaTotal = gameTime.ElapsedGameTime.TotalSeconds;
            float timeLeft = 0;

            //loop while there is time left for the animations
            #region Loop For Animations
            do
            {
                CalculateMovement(playerTransform);
                double delta = deltaTotal;

                #region State Machine
                switch (state)
                {
                    //entry point only
                    case States.Start:
                        state = States.StanceStart;
                        delta = 0;
                        break;

                    //start up the stance animation and go immediatly to the next state
                    case States.StanceStart:
                        animatedModel.PlayClip("walkstart");
                        animatedModel.Update(0);
                        animatedModel.Speed = 1;
                        state = States.Stance;
                        delta = 0;
                        break;

                    //wait for something to happen (a return value greater than 0 means something happened)
                    case States.Stance:
                        break;

                    //start up the walk animation and go immediatly to the next state
                    case States.WalkStart:
                        animatedModel.PlayClip("walkstart");
                        animatedModel.Speed = 1;
                        state = States.WalkLoop;
                        delta = 0;
                        break;

                    //the alien has a pie and should start eating it
                    case States.EatStart:
                        animatedModel.Speed = 1;
                        animatedModel.PlayClip("catcheat");
                        delta = 0;
                        state = States.Eating;
                        break;

                    //the alien is eating the pie
                    case States.Eating:
                        if (!animatedModel.Player.FinishedPlaying)
                            animatedModel.Update(deltaTotal);
                        else
                            state = States.WalkStart;
                        break;

                    //continue playing the walk loop animation until time runs out or there is new input
                    case States.WalkLoop:
                        timeLeft = WalkLoop(delta);
                        break;

                    //start up the walk loop animation and go immediatly to the next state
                    case States.WalkLoopStart:
                        animatedModel.PlayClip("walkstart");
                        state = States.WalkLoop;
                        delta = 0;
                        break;
                }
                #endregion

                //if timeLeft has something in it and its not bigger than delta 
                //(which happens when the animations are not restarted), 
                //update delta to be the amount of time used 
                if (timeLeft != 0 && timeLeft < delta)
                    delta -= timeLeft;
                //only do location and orientation updates if victoria is in her final position
                //for this screen refresh
                if (delta == deltaTotal)
                {

                    //
                    // Part 1:  Compute a new orientation
                    //

                    Matrix deltaMatrix = animatedModel.DeltaMatrix;
                    float deltaAngle = (float)Math.Atan2(deltaMatrix.Backward.X, deltaMatrix.Backward.Z);
                    float newOrientation = orientation + deltaAngle;

                    //
                    // Part 2:  Compute a new location
                    //


                    // We are likely rotated from the angle the model expects to be in
                    // Determine that angle.
                    Matrix rootMatrix = animatedModel.RootMatrix;
                    float actualAngle = (float)Math.Atan2(rootMatrix.Backward.X, rootMatrix.Backward.Z);
                    Vector3 move = Vector3.TransformNormal(animatedModel.DeltaPosition, Matrix.CreateRotationY(newOrientation - actualAngle));

                    Vector3 newLocation = location + move;
                    newLocation.Y = 0;

                    #region Region specific and collision managment

                    string region = phibesModel.TestRegion(newLocation);
                    if (region.StartsWith("R_Door"))
                    {
                        inDoor = true;
                        foreach (PrisonModel pm in game.PhibesModel)
                        {
                            foreach (int door in pm.DoorToBone.Keys)
                            {
                                //if the door region number == the door bone
                                if (Convert.ToInt32(region.Substring(region.Length - 1)) == door)
                                {
                                    bool doorOpen = phibesModel.TestDoor(door, pm, newLocation, (float)delta, location, orientation);
                                    if ((doorOpen || region != oldRegion))
                                        location = newLocation;

                                }
                            }
                        }
                    }

                    else if (region != "" &&
                        (stateWalk == StatesWalk.WalkAndTurnLeft || stateWalk == StatesWalk.WalkAndTurnRight))
                    {
                        location = newLocation;
                    }

                    if (!region.StartsWith("R_Door"))
                    {
                        inDoor = false;
                    }

                    oldRegion = region;

                    
                    if (!region.StartsWith("R_Door") && !leaveDoorsOpen)
                    {
                        //continue closing all doors if they are in the process of closing and the player is not in a door
                        foreach (PrisonModel pm in game.PhibesModel)
                        {
                            foreach (int door in pm.DoorToBone.Keys)
                            {
                                pm.DoorOpen[door] = false;
                                pm.closeDoor(door, (float)delta);
                            }
                        }
                    }

                    if (region == "R_Section1")
                    {
                        animatedModel.Section = 1;
                    }
                    else if (region == "R_Section6")
                    {
                        animatedModel.Section = 6;
                    }
                    else if (region == "R_Section5")
                    {
                        animatedModel.Section = 5;
                    }
                    else if (region == "R_Section2")
                    {
                        animatedModel.Section = 2;
                    }
                    else if (region == "R_Section3")
                    {
                        animatedModel.Section = 3;
                    }
                    else if (region == "R_Section4")
                    {
                        animatedModel.Section = 4;
                    }

                    #endregion

                    orientation = newOrientation;
                    SetPlayerTransform();
                }

                deltaTotal -= delta;

            } while (deltaTotal > 0);
            #endregion

            #region Handle Alien spit
            //limit the spits to every 3 seconds
            //don't shoot spit when eating a pie
            if ((gameTime.TotalGameTime - lastShot) >= new TimeSpan(0, 0, 0, 0, 3000) &&
                (state != States.Eating))
            {
                int mouthBone = animatedModel.Model.Bones["Bip01 Head"].Index;
                Vector3 spitDirection = animatedModel.GetBoneAbsoluteTransform(mouthBone).Backward;
                float spitOrientation = (float)Math.Atan2(spitDirection.X, spitDirection.Z);
                Vector3 spitStartPosition = animatedModel.GetBoneAbsoluteTransform(mouthBone).Translation;
                victoria.SpitFire.Fire(Matrix.CreateRotationY(spitOrientation) * Matrix.CreateTranslation(spitStartPosition) * transform,
                    spitOrientation, spitStartPosition);
                lastShot = gameTime.TotalGameTime;
            }
            #endregion

            //update the pie attached to the alien
            if (pie != null)
            {
                int handbone = animatedModel.Model.Bones["Bip01 L Hand"].Index;

                Matrix piePosition = Matrix.CreateTranslation(20, 0, 0) *
                    animatedModel.GetBoneAbsoluteTransform(handbone) *
                    transform;

                pie.Transform = piePosition;
                //start the animation if it has not already started
                if (state != States.Eating)
                    state = States.EatStart;

                //delete the pie at time = 2.5 in clip
                else if (animatedModel.Player.Time >= 2.5)
                {
                    pie = null;
                }
            }
        }

        /// <summary>
        /// Handles the animation and input while in the WalkLoop State, which is 
        /// the state that handles forward animation between animation restarts.
        /// It returns the time left over, if any, after updating the animation
        /// </summary>
        /// <param name="keyboardState"></param>
        /// <param name="delta"></param>
        /// <returns></returns>
        float WalkLoop(double delta)
        {
            if (stateWalk == StatesWalk.Stop)
            {
                state = States.StanceStart;
                return 0;
            }

            //update orientation
            if (stateWalk == StatesWalk.WalkAndTurnLeft ||
                stateWalk == StatesWalk.TurnLeft)
            {
                orientation += (float)(delta);
            }
            if (stateWalk == StatesWalk.WalkAndTurnRight ||
                stateWalk == StatesWalk.TurnRight)
            {
                orientation -= (float)(delta);
            }

            //if animation ended, restart it, and return how far into the next animation we should be
            float timeLeft = animatedModel.Update(delta);
            if (timeLeft > 0)
            {
                state = States.WalkLoopStart;
            }
            return timeLeft;
        }

        public void Draw(GraphicsDeviceManager graphics, GameTime gameTime, PieFire pieFire)
        {
            Matrix transform = Matrix.CreateRotationY(orientation);
            transform.Translation = location;

            animatedModel.Draw(graphics, gameTime, transform);

            if (pie != null)
            {
                pieFire.DrawModel(graphics, pie.Transform, pie.PieDisplay);
            }
        }

        /// <summary>
        /// Set the value of transform to match the current location
        /// and orientation.
        /// </summary>
        public void SetPlayerTransform()
        {
            transform = Matrix.CreateRotationY(orientation);
            transform.Translation = location;
        }
    }
}
